///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file wfn_data.h
///
/// \brief   header file for the wavefunction data class
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef PCPS_WFN_DATA_HEADER
#define PCPS_WFN_DATA_HEADER

#include<src/pcps.h>
#include<src/input.h>
#include<src/command.h>
#include<src/mpi_interface.h>

namespace pcps {

//  /// \brief  function to write an object to text with leading and trailing whitespace
//  template <class T> inline void text_writeout(std::stringstream & ss, const T & val) {
//    ss << " " << val << " " << std::endl;
//  }
//  inline void text_writeout(std::stringstream & ss, const double & val) {
//    ss << " " << std::setprecision(15) << std::scientific << val << " " << std::endl;
//  }
//  inline void text_writeout(std::stringstream & ss, const std::complex<double> & val) {
//    ss << " " << std::setprecision(15) << std::scientific << val << " " << std::endl;
//  }

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief   Class to hold wavefunction data.
  ///
  /// This class holds the raw data used to define the wavefunction, which may be considered
  /// variable or fixed.  In addition, objects of this class store the position of their data
  /// in the global ordering of wavefunction variables and constants.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  template<class S> class WfnData {

    private:

      /// \brief  a name for this data object
      std::string _name;

      /// \brief  whether the data is fixed or variable
      bool _fixed;

      /// \brief  the data
      std::vector<S> _data_vec;

      /// \brief  position of the data in the global ordering of wavefunction data.
      ///         Positions >= 0 indicate varaible data, while positions < 0 indicate constant data.
      int _data_pos;

      /// \brief  assignment operator is disabled
      WfnData & operator=(const WfnData &);

      /// \brief  copy constructor is disabled
      WfnData(const WfnData &);

    public:

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief returns the name of the data object
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      const std::string & name() const { return this->_name; }

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief returns whether the data is fixed or variable
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      bool fixed() const { return this->_fixed; }

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief returns the position of the data in the overall data ordering
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      int data_pos() const { return this->_data_pos; }

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief returns the data as a constant reference
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      const std::vector<S> & data_vec() const { return this->_data_vec; }

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief returns the data by reference
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      std::vector<S> & data_vec() { return this->_data_vec; }

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief returns the size of the data vector
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      size_t size() const { return this->_data_vec.size(); }

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief sets the position of the data in the global data ordering
      ///
      /// \param[in]      pos   the position of the data elements in the global ordering
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      void set_pos(const int pos) { _data_pos = pos; }

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief constructs the data object
      ///
      /// \param[in,out]  arch     an archive holding the data object
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      WfnData(pcps::Archive & arch) {

        // read and broadcast the name, fixed/variable flag, and data vector
        pcps::mpi::read_and_bcast(arch, _name, "Failed to read name in WfnData constructor.");
        pcps::mpi::read_and_bcast(arch, _fixed, "Failed to read fixed/variable flag in WfnData constructor.");
        pcps::mpi::read_and_bcast(arch, _data_vec, "Failed to read data vector in WfnData constructor.");

      }

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief converts the object into the command that will create the object
      ///
      /// \param[in,out]  arch     an archive in which to store the command
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      void to_command(pcps::Archive & arch) const {

        // write keyword for WfnDataCommand
        arch << pcps::WfnDataCommand<S>::keyword() << std::endl;

        // write the name
        arch << _name << std::endl;

        // write whether the data is fixed or variable
        arch << _fixed << std::endl;

        // write the data
        arch << _data_vec << std::endl;

      }

  };

}

template<class T>
inline pcps::Archive & operator<<(pcps::Archive & arch, const pcps::WfnData<T> & w) { w.to_command(arch); return arch; }

#endif
